/*****************************************************************
Wowengine - v0.1

File: wen_spatial.cpp
Creation date: 2012-10-15
*****************************************************************/

#include "wen_spatial.h"

using namespace wen;

Spatial::Spatial()
:
m_pkParent(NULL),
m_bVisible(true)
{
	D3DXMatrixIdentity(&m_localTransform);
	D3DXMatrixIdentity(&m_worldTransform);

	m_kPosition.x = 0;
	m_kPosition.y = 0;
	m_kPosition.z = 0;

	m_kScale.x = 0;
	m_kScale.y = 0;
	m_kScale.z = 0;

	m_kRotation.x = 0;
	m_kRotation.y = 0;
	m_kRotation.z = 0;
	m_kRotation.w = 0;

	m_kRotationAxis.x = 0;
	m_kRotationAxis.y = 0;
	m_kRotationAxis.z = 0;
}

Spatial::~Spatial()
{	
}

Spatial* Spatial::GetParent() const
{
	return m_pkParent;
}

void Spatial::SetParent(Spatial* parent)
{
	m_pkParent = parent;	
}

const D3DXMATRIX* Spatial::GetWorldTransformation() const
{
	return &m_worldTransform;
}

const D3DXMATRIX* Spatial::GetLocalTransformation() const
{
	return &m_localTransform;
}

void Spatial::Draw(Render& rkRender)
{
	rkRender.SetMatrixMode(WORLD);

	D3DXMatrixIdentity(&m_worldTransform);

	//D3DXMatrixTransformation(&m_localTransform, NULL, NULL, &m_kScale, NULL, &m_kRotation, &m_kPosition);
	
	UpdateWorldTransformInfo();

	rkRender.SetTransformation(m_worldTransform);
}

void Spatial::Update(float fTimeBetweenFrames)
{
}

void Spatial::UpdateWorldTransformInfo()
{
	D3DXMATRIX parentWorldMatrix;
	D3DXMatrixIdentity(&parentWorldMatrix);

	if (m_pkParent) 
	{		
		parentWorldMatrix = *m_pkParent->GetWorldTransformation();
	} 
	else 
	{		
		parentWorldMatrix = m_localTransform;
	}
	
	D3DXMATRIX theMatrix;	
	D3DXMatrixMultiply(&theMatrix, &m_localTransform, &parentWorldMatrix);

	m_worldTransform = theMatrix;
}

void Spatial::SetPos(float fPosX, float fPosY, float fPosZ) 
{
	m_kPosition.x = fPosX;
	m_kPosition.y = fPosY;
	m_kPosition.z = fPosZ;
	
	D3DXMatrixTransformation(&m_localTransform, NULL, NULL, &m_kScale, NULL, &m_kRotation, &m_kPosition);
}

void Spatial::SetRotation(float fRotationX, float fRotationY, float fRotationZ) 
{
	m_kRotationAxis.x = fRotationX;
	m_kRotationAxis.y = fRotationY;
	m_kRotationAxis.z = fRotationZ;

	D3DXQuaternionRotationYawPitchRoll(&m_kRotation, fRotationZ, fRotationY, fRotationX); //Create a Quaternion to use on D3DXMatrixTransformation

	D3DXMatrixTransformation(&m_localTransform, NULL, NULL, &m_kScale, NULL, &m_kRotation, &m_kPosition);	
}

void Spatial::SetRotationByQuaternion(float fRotationX, float fRotationY, float fRotationZ, float fRotationW)
{
	m_kRotation.x = fRotationX;
	m_kRotation.y = fRotationY;
	m_kRotation.z = fRotationZ;
	m_kRotation.w = fRotationW;

	D3DXMatrixTransformation(&m_localTransform, NULL, NULL, &m_kScale, NULL, &m_kRotation, &m_kPosition);
}

void Spatial::SetDim(float fWidth, float fHeight, float fDepth) 
{ 
	m_kScale.x = fWidth;
	m_kScale.y = fHeight;
	m_kScale.z = fDepth;

	D3DXMatrixTransformation(&m_localTransform, NULL, NULL, &m_kScale, NULL, &m_kRotation, &m_kPosition);	
}

void Spatial::CalculateMatrix()
{
	D3DXMatrixTransformation(&m_localTransform, NULL, NULL, &m_kScale, NULL, &m_kRotation, &m_kPosition);	
}

bool Spatial::CheckBVHFrustumCollision(BVH* theBvh, Render& theRender)
{
	bool visible = false;

	D3DXVECTOR3* maxVertex = theBvh->GetMaxVertex();
	D3DXVECTOR3* minVertex = theBvh->GetMinVertex();

	bool maxVertexVisible = theRender.CheckIfVertexIsInsideFrustum(maxVertex->x, maxVertex->y, maxVertex->z);
	bool minVertexVisible = theRender.CheckIfVertexIsInsideFrustum(minVertex->x, minVertex->y, minVertex->z);

	/*if (maxVertexVisible && minVertexVisible)
	{
		visible = true;
	}
	else */
	if (maxVertexVisible || minVertexVisible)
	{
		visible = true;
	}
	else
	{
		visible = false;
	}

	return visible;
}

void Spatial::CalculateBVH(Render& rkRender)
{
	if (type == TYPE_MESH)
	{	
		BVH* bvh = GetBVH();

		D3DXVECTOR3 firstVertex;
		firstVertex.x = bvh->m_iMaxX;
		firstVertex.y = bvh->m_iMaxY;
		firstVertex.z = bvh->m_iMaxZ;
		D3DXVECTOR3 XYZ;
		D3DXVec3TransformCoord(&XYZ, &firstVertex, this->GetParent()->GetWorldTransformation());		

		D3DXVECTOR3 secondVertex;
		secondVertex.x = bvh->m_iMinX;
		secondVertex.y = bvh->m_iMinY;
		secondVertex.z = bvh->m_iMinZ;
		D3DXVECTOR3 XYZ2;
		D3DXVec3TransformCoord(&XYZ2, &secondVertex, this->GetParent()->GetWorldTransformation());		

		D3DXVECTOR3 thirdVertex;
		thirdVertex.x = bvh->m_iMaxX;
		thirdVertex.y = bvh->m_iMinY;
		thirdVertex.z = bvh->m_iMinZ;
		D3DXVECTOR3 XYZ3;
		D3DXVec3TransformCoord(&XYZ3, &thirdVertex, this->GetParent()->GetWorldTransformation());		

		D3DXVECTOR3 fourthVertex;
		fourthVertex.x = bvh->m_iMaxX;
		fourthVertex.y = bvh->m_iMaxY;
		fourthVertex.z = bvh->m_iMinZ;
		D3DXVECTOR3 XYZ4;
		D3DXVec3TransformCoord(&XYZ4, &fourthVertex, this->GetParent()->GetWorldTransformation());		

		D3DXVECTOR3 fifthVertex;
		fifthVertex.x = bvh->m_iMaxX;
		fifthVertex.y = bvh->m_iMinY;
		fifthVertex.z = bvh->m_iMaxZ;
		D3DXVECTOR3 XYZ5;
		D3DXVec3TransformCoord(&XYZ5, &fifthVertex, this->GetParent()->GetWorldTransformation());		

		D3DXVECTOR3 sixVertex;
		sixVertex.x = bvh->m_iMinX;
		sixVertex.y = bvh->m_iMaxY;
		sixVertex.z = bvh->m_iMaxZ;
		D3DXVECTOR3 XYZ6;
		D3DXVec3TransformCoord(&XYZ6, &sixVertex, this->GetParent()->GetWorldTransformation());		

		D3DXVECTOR3 seventhVertex;
		seventhVertex.x = bvh->m_iMinX;
		seventhVertex.y = bvh->m_iMinY;
		seventhVertex.z = bvh->m_iMaxZ;
		D3DXVECTOR3 XYZ7;
		D3DXVec3TransformCoord(&XYZ7, &seventhVertex, this->GetParent()->GetWorldTransformation());		

		D3DXVECTOR3 eigthVertex;
		eigthVertex.x = bvh->m_iMinX;
		eigthVertex.y = bvh->m_iMaxY;
		eigthVertex.z = bvh->m_iMinZ;
		D3DXVECTOR3 XYZ8;
		D3DXVec3TransformCoord(&XYZ, &eigthVertex, this->GetParent()->GetWorldTransformation());		


		bool XYZfrustum = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ);
		bool xyzfrustum = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ2);
		bool xyzfrustum2 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ3);
		bool xyzfrustum3 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ4);
		bool xyzfrustum4 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ5);
		bool xyzfrustum5 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ6);
		bool xyzfrustum6 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ7);
		bool xyzfrustum7 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ8);

		if (XYZfrustum && xyzfrustum && xyzfrustum2 && xyzfrustum3 && xyzfrustum4 && xyzfrustum5 && xyzfrustum6 && xyzfrustum7)
		{
			m_bVisible = true;
		}
		else if (XYZfrustum || xyzfrustum || xyzfrustum2 || xyzfrustum3 || xyzfrustum4 || xyzfrustum5 || xyzfrustum6 || xyzfrustum7)
		{
			m_bVisible = true;
		}
		else
		{
			m_bVisible = false;
		}
	}
	else
	{
		BVH* myBvh = this->GetBVH();
		D3DXVECTOR3 XYZ(myBvh->m_iMaxX, myBvh->m_iMaxY, myBvh->m_iMaxZ);
		D3DXVECTOR3 xyz(myBvh->m_iMinX, myBvh->m_iMinY, myBvh->m_iMinZ);
		D3DXVECTOR3 xyz2(myBvh->m_iMaxX, myBvh->m_iMinY, myBvh->m_iMinZ);
		D3DXVECTOR3 xyz3(myBvh->m_iMaxX, myBvh->m_iMaxY, myBvh->m_iMinZ);
		D3DXVECTOR3 xyz4(myBvh->m_iMaxX, myBvh->m_iMinY, myBvh->m_iMaxZ);
		D3DXVECTOR3 xyz5(myBvh->m_iMinX, myBvh->m_iMaxY, myBvh->m_iMaxZ);
		D3DXVECTOR3 xyz6(myBvh->m_iMinX, myBvh->m_iMinY, myBvh->m_iMaxZ);
		D3DXVECTOR3 xyz7(myBvh->m_iMinX, myBvh->m_iMaxY, myBvh->m_iMinZ);
		//D3DXVec3TransformCoord(&xyz7, &lclxyz7, &this->m_kworldMatrix);

		bool XYZfrustum = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(XYZ);
		bool xyzfrustum = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(xyz);
		bool xyzfrustum2 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(xyz2);
		bool xyzfrustum3 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(xyz3);
		bool xyzfrustum4 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(xyz4);
		bool xyzfrustum5 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(xyz5);
		bool xyzfrustum6 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(xyz6);
		bool xyzfrustum7 = rkRender.GetFrustum()->CheckIfPointIsInsideFrustum(xyz7);

		if (XYZfrustum && xyzfrustum && xyzfrustum2 && xyzfrustum3 && xyzfrustum4 && xyzfrustum5 && xyzfrustum6 && xyzfrustum7)
		{
			m_bVisible = true;
		}
		else if (XYZfrustum || xyzfrustum || xyzfrustum2 || xyzfrustum3 || xyzfrustum4 || xyzfrustum5 || xyzfrustum6 || xyzfrustum7)
		{
			m_bVisible = true;
		}
		else
		{
			m_bVisible = false;
		}
	}
}